package mrt.cse.tempworks.initialWorksOfTeamMembers.shameera;

import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/*
This class connect to the given server , run a check for lead
manager periodically when it is triggered node change in parent
node
*/

public abstract class NodeWatcher implements Watcher {

    //TODO- add comments for necessary method and handle exceptions well

    private final int SESSION_TIME_OUT = 5000;
    ZooKeeper zk;
    protected final String defPath = "/manager";
    protected boolean isLeader = false;
    protected String leaderPath;
    List<String> managersList = new ArrayList<String>();
    private long leaderId=0;
    public String name;

    
    
    public void connectToServer(String host) {
        try {
            zk = new ZooKeeper(host, SESSION_TIME_OUT, this);
        } catch (IOException e) {
            System.out.println(e.toString());
        }
    }

    protected void invokeProcess(String name){
        Stat stat= isNodeExist(defPath, true);
        if(stat!=null){
            createZnode(defPath+"/"+name , name);
            List<String> childList = getChildernOfMainNode();
            checkLeader(childList);
        }

    }

    protected void checkLeader(List<String> childList){
        if (childList.size()==1) {
            assignLeaderMySelf(childList.get(0) , getZidOfNode(childList.get(0)));

        }else{
            processForLeader(childList);
        }
    }

    protected  void addWatcher(){

        List<String> childList = getChildernOfMainNode();
        checkLeader(childList);
    }
    
    
    protected void createZnode(String path , String data) {
        try {
            zk.create(path, data.getBytes(), ZooACL.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
            System.out.println("node created in " + path);
        } catch (KeeperException e) {
            System.out.println(e.toString());
        } catch (InterruptedException e) {
            System.out.println(e.toString());
        }
    }
    
    protected List<String> getChildernOfMainNode() {
        try {
            return zk.getChildren(defPath, this);
        } catch (KeeperException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        } catch (InterruptedException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }
        return new ArrayList<String>();
    }
    
    
    protected void processForLeader(List<String> newList){
        boolean leaderExist=false;
        //TODO- check whether is this the current leader then it is not needed to run election process
    /*    if(leaderPath!=null){
            for(String st : newList) {
                if (leaderPath.equalsIgnoreCase(st)) {
                    leaderExist = true;
                    break;
                }
            }
        }*/
        
        if(! leaderExist){
            electLeadManager(newList);
        }
    }

    protected void assignLeaderMySelf(String path , String leaderId){
        this.leaderId = Long.valueOf(leaderId);
        this.leaderPath = leaderPath;
        isLeader =true;
        System.out.println("Leader is : " + name);
    }
    
    protected Stat isNodeExist(String path, boolean addWatch){

        if(path!=null){
            try {
            if(addWatch) {
                return zk.exists(path, this);
            }else {
                return zk.exists(path, false);
            }
            } catch (KeeperException e) {
                e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
            } catch (InterruptedException e) {
                e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
            }
        }
        return null;
    }


    protected boolean electLeadManager(List<String> newList){
        //TODO- this process crash when sequence id is higher than a long value do something for that
        System.out.println(name + " : start to elect manager");
        int count=0;
        long tempId;
        long tempLeaderId =0;
        String tempLeaderPath = "";
        for(String str : newList) {
            tempId = Long.valueOf(getZidOfNode(str));
            if (count == 0) {
                tempLeaderId=tempId;
                count++;
            }
            System.out.println("temp value : " + tempId);
            if(tempId!= 0) {
                if (tempId <= tempLeaderId) {
                    tempLeaderId = tempId;
                    tempLeaderPath = str;
                }
            }
        }
//        System.out.println(name + " : finished election  tempLeaderId is : " + tempLeaderId +
//                "     and tempLeaderPath is : " + tempLeaderPath);
        if(tempLeaderId > 0 && ! tempLeaderPath.equalsIgnoreCase("")){
            leaderId = tempLeaderId;
            leaderPath = tempLeaderPath;
            System.out.println( name + " : Leader path is : " + leaderPath + "       Leader Id is : " + leaderId);
            return true;
        }else{
            return false;
        }
    }


    protected String getZidOfNode(String path){
        int len;
        if(path!= null){
            len = path.length();
            if(len>10) {
                return path.substring(len - 10, len);
            }
        }
        return "";
    }
    
    @Override
    public void process(WatchedEvent event) {
//        System.out.println(event);
        if (event.getType() == Event.EventType.NodeCreated) {
            System.out.println(name + " : new node create under path : " + event.getPath());
        }else if (event.getType() == Event.EventType.NodeDeleted) {
            System.out.println(name + " : node deleted in : " + event.getPath());
        }else if (event.getType() == Event.EventType.NodeChildrenChanged) {
            System.out.println(name + " :childern change in - " + event.getPath());
            addWatcher();
        }
    }
    
    
    public void refreshManagers(List<String> liveManagersList){
        
    }
}
